﻿/*
Copyright (c) 2010, hiSoft
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the hiSoft nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE HISOFT AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL HISOFT BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;

namespace Orchard.Test.Library
{
    public class Blog : UIHelperBase
    {
        public BlogHelper BlogHelper;

        public BlogSettings BlogSetting { get; private set; }

        public string Title { get { return BlogSetting.Title; } }

        public string Permalink { get { return BlogSetting.Permalink; } }

        public string MenuText { get { return BlogSetting.MenuText; } }

        public string AdminMenuText { get { return BlogSetting.AdminMenuText; } }

        public string Owner { get { return BlogSetting.Owner; } }

        public Blog(TestLibrary settings, BlogSettings blogSetting)
            : base(settings)
        {
            BlogHelper = new BlogHelper(settings);
            BlogSetting = blogSetting;
        }

        private void InputSetting_Tag(string[] tagNames)
        {
            InputPostSetting("", "", tagNames, false, true, "", PublishSettings.SaveDraft, "", "");
        }

        private void InputSetting_Title(string title)
        {
            InputPostSetting(title, "", null, false, true, "", PublishSettings.SaveDraft, "", "");
        }

        private void InputSetting_Title(string title,PublishSettings publishSetting)
        {
            InputPostSetting(title, "", null, false, true, "", publishSetting, "", "");
        }

        private void InputPostSetting(string postTitle,string permalink,string[] tagNames,bool setAsHomePage,bool allowNewComments,string owner,PublishSettings publishSetting,string publishDate,string publishTime)
        {
            if (postTitle != "") selenium.Type("Routable_Title", postTitle);
            if (permalink != "") selenium.Type("Routable_Slug", permalink);

            if (tagNames != null && tagNames.Length != 0)
            {
                string tagsString = null;
                for (int i = 0; i < tagNames.Length; i++)
                {
                    tagsString += tagNames[i] + ",";
                }
                selenium.Type("Tags_Tags", tagsString);
            }

            if (setAsHomePage)
            {
                selenium.Check("Routable_PromoteToHomePage");
            }
            else
            {
                selenium.Uncheck("Routable_PromoteToHomePage");
            }

            if (allowNewComments)
            {
                selenium.Check("CommentsActive");
            }
            else
            {
                selenium.Uncheck("CommentsActive");
            }
            if (owner != "")
            {
                if (owner != TestLibrary.Consts.AdminUserName)
                {
                    selenium.Type(OwnerLocator, owner);
                }
            }

            switch (publishSetting)
            {
                case PublishSettings.SaveDraft:
                    ClickAndWait("//button[@value='submit.Save']");
                    break;
                case PublishSettings.PublishNow:
                    ClickAndWait("//button[@value='submit.Publish']");
                    break;
                case PublishSettings.PublishLater:                   
                    if (!String.IsNullOrEmpty(publishDate))
                    {
                        selenium.Type("PublishLater_ScheduledPublishDate", publishDate);
                    }
                    if (!String.IsNullOrEmpty(publishTime))
                    {
                        selenium.Type("PublishLater_ScheduledPublishTime", publishTime);
                    }
                    ClickAndWait("//button[@value='submit.PublishLater']");
                    break;
            }

        }

        private void InputPostSetting(PostSettings postSetting)
        {
            if (postSetting == null)
            {
                throw new CaseErrorException(new ArgumentNullException("postSetting"));
            }

            selenium.Type("Routable_Title", postSetting.Title);  
            selenium.Type("Routable_Slug", postSetting.Permalink);

            if (postSetting.Tags != null && postSetting.Tags.Count != 0)
            {
                string tagsString = null;
                for (int i = 0; i < postSetting.Tags.Count; i++)
                {
                    tagsString += postSetting.Tags[i].Name + ",";
                }
                selenium.Type("Tags_Tags", tagsString);
            }

            if (postSetting.SetAsHomePage)
            {
                selenium.Check("Routable_PromoteToHomePage");
            }
            else
            {
                selenium.Uncheck("Routable_PromoteToHomePage");
            }

            if (postSetting.AllowNewComments)
            {
                selenium.Check("CommentsActive");
            }
            else
            {
                selenium.Uncheck("CommentsActive");
            }

            if (postSetting.Owner != TestLibrary.Consts.AdminUserName)
            {
                selenium.Type(OwnerLocator, postSetting.Owner);
            }

            if (postSetting.ArchiveLater)
            {
                selenium.Check("ArchiveLater_Command_ArchiveLater");
                if (!string.IsNullOrEmpty(postSetting.ArchiveDate))
                selenium.Type("ArchiveLater_ScheduledArchiveDate", postSetting.ArchiveDate);
                if (!string.IsNullOrEmpty(postSetting.ArchiveTime))
                selenium.Type("ArchiveLater_ScheduledArchiveTime", postSetting.ArchiveTime);             
            }           

            switch (postSetting.PublishSetting)
            {
                case PublishSettings.SaveDraft:
                    ClickAndWait("//button[@value='submit.Save']");
                    break;
                case PublishSettings.PublishNow:
                    ClickAndWait("//button[@value='submit.Publish']");
                    break;
                case PublishSettings.PublishLater:                    
                    selenium.Type("PublishLater_ScheduledPublishDate", postSetting.PublishDate);
                    selenium.Type("PublishLater_ScheduledPublishTime", postSetting.PublishTime);
                    ClickAndWait("//button[@value='submit.PublishLater']");
                    break;  
                case PublishSettings.None:
                    break;
            }            
         
            postSetting.Permalink = selenium.GetValue("Routable_Slug");
        }

        private int GetPostIndexInDefaultBlog(string postTitle)
        {
            var count = (int)selenium.GetXpathCount(PostsLocator);
            var index = 1;
            for (; index <= count; ++index)
            {
                string path = String.Format("{0}[{1}]/div/div/h3/a", PostsLocator, index);
                var text = selenium.GetText(path);
                if (String.CompareOrdinal(postTitle, text) == 0)
                {
                    break;
                }
            }
            if (index > count)
                throw new CaseFailureException(String.Format("Can't find post {0} in default blog!", postTitle));
            return index;
        }

        private int GetPostIndexOnManagePostsPage(Post post)
        {
            var count = (int)selenium.GetXpathCount(PostsLocator);
            var index = 1;
            for (; index <= count; ++index)
            {
                string path = String.Format("{0}[{1}]/div/div/h3/a", PostsLocator, index);
                var text = selenium.GetText(path);
                if (String.CompareOrdinal(post.Title, text) == 0)
                {
                    break;
                }
            }

            if (index > count)
                throw new CaseFailureException(String.Format("Can't find post {0} in blog {1} manage page!", post.Title, post.ContainerBlog.Title));

            return index;
        }


        private int GetPostIndexOnManagePostsPage(string postTitle)
        {
            var count = (int)selenium.GetXpathCount(PostsLocator);
            var index = 1;
            for (; index <= count; ++index)
            {
                string path = String.Format("{0}[{1}]/div/div/h3/a", PostsLocator, index);
                var text = selenium.GetText(path);
                if (String.CompareOrdinal(postTitle, text) == 0)
                {
                    break;
                }
            }

            if (index > count)
                throw new CaseFailureException(String.Format("Can't find post {0} in blog manage page!", postTitle));

            return index;
        }

        private int GetActionIndexOfPostByText(int postIndex, string actionText)
        {
            var count = selenium.GetXpathCount(String.Format("{0}[{1}]/div/div[2]/a", PostsLocator, postIndex));
            for (int i = 1; i <= count; i++)
            {
                if (selenium.GetText(String.Format("{0}[{1}]/div/div[2]/a[{2}]", PostsLocator, postIndex, i)) == actionText)
                {
                    return i;
                }
            }
            return -1;
        }

        public void ManagePosts()
        {
            BlogHelper.DoActionToBlog(this, BlogAction.ListPosts);
        }

        public bool IsPostPresent(Post post)
        {
            ManagePosts();
            return selenium.IsElementPresent(String.Format("link={0}", post.Title));
        }

        public bool IsPostPresent(string postTitle)
        {
            ManagePosts();
            return selenium.IsElementPresent(String.Format("link={0}", postTitle));
        }    

        public Post CreateRandomPost()
        {         
            return CreatePost(new PostSettings());
        }      

        public Post CreatePost(PostSettings postSetting)
        {
            BlogHelper.DoActionToBlog(this, BlogAction.NewPost);          
            InputPostSetting(postSetting);  
            var post = new Post(postSetting, this);
            string editPostString = selenium.GetLocation();
            string[] postIdStrings = editPostString.Split('/');
            int postId = 0;
            Int32.TryParse(postIdStrings[postIdStrings.Length - 2], out postId);
            post.Id = postId; 
            return post;
        }

        public Post EditPost(Post post, PostSettings newSetting)
        {
            DoActionToPost(post, PostAction.Edit);
            InputPostSetting(newSetting);
            return new Post(newSetting, this);
        }

        public void EditAndSavePost_Tags(string postTitle, string[] tagsNames)
        {
            DoActionToPost(postTitle, PostAction.Edit);
            InputSetting_Tag(tagsNames);    
        }

        public void EditPost_Title(string postTitle, string title,PublishSettings publishSetting)
        {
            DoActionToPost(postTitle, PostAction.Edit);
            InputSetting_Title(title, publishSetting);
        }    

        public Post TranslatePost(Post post, string language)
        {
            ManagePosts();
            var postIndex = GetPostIndexOnManagePostsPage(post);
            ClickAndWait(String.Format("{0}[{1}]/div/div[3]/div/div/a", PostsLocator, postIndex));

            if (!String.IsNullOrEmpty(language))
            {
                selenium.Select("SelectedCulture", String.Format("label={0}", language));
            }
            var routableTitle = String.Format("{0} {1}", post.Title, language);
            selenium.Type("Routable_Title", routableTitle);
            string permalink = String.Format("{0}-{1}", post.Permalink, language);
            selenium.Type("Routable_Slug", permalink);
            ClickAndWait("//button[@value='submit.Publish']");

            var postSettings = new PostSettings();
            postSettings.Title = routableTitle;
            postSettings.Permalink = permalink;
            var translatedPost = new Post(postSettings, post.ContainerBlog);
            return translatedPost;
        }

        public void DoActionToPost(Post post, PostAction action)
        {
            ManagePosts();
            var postIndex = GetPostIndexOnManagePostsPage(post);
            string actionText = null;
            switch (action)
            {
                case PostAction.Edit:
                    actionText = "Edit";
                    break;
                case PostAction.Publish:
                    actionText = "Publish";
                    break;
                case PostAction.Delete:
                    actionText = "Delete";
                    break;
                case PostAction.Unpublish:
                    actionText = "Unpublish";
                    break;
                case PostAction.View:
                    actionText = "View";
                    break;
            }
            var actionIndex = GetActionIndexOfPostByText(postIndex, actionText);
            ClickAndWait(String.Format("{0}[{1}]/div/div/a[{2}]", PostsLocator, postIndex, actionIndex));
        }

        public void DoActionToPost(string postTitle, PostAction action)
        {
            ManagePosts();
            var postIndex = GetPostIndexInDefaultBlog(postTitle);
            string actionText = null;
            switch (action)
            {
                case PostAction.Edit:
                    actionText = "Edit";
                    break;
                case PostAction.Publish:
                    actionText = "Publish";
                    break;
                case PostAction.Delete:
                    actionText = "Delete";
                    break;
                case PostAction.Unpublish:
                    actionText = "Unpublish";
                    break;
                case PostAction.View:
                    actionText = "View";
                    break;
            }
            var actionIndex = GetActionIndexOfPostByText(postIndex, actionText);
            ClickAndWait(String.Format("{0}[{1}]/div/div/a[{2}]", PostsLocator, postIndex, actionIndex));
        }

        public void DoSelectActionToPost(Post post, PostAction action)
        {
            ManagePosts();
            int index = GetPostIndexOnManagePostsPage(post);
            selenium.Check(String.Format("{0}[{1}]/div/div/input", PostsLocator, index));
            switch (action)
            {
                case PostAction.Publish:
                    selenium.Select("publishActions", "label=Publish Now");
                    break;
                case PostAction.Unpublish:
                    selenium.Select("publishActions", "label=Unpublish");
                    break;
                case PostAction.Delete:
                    selenium.Select("publishActions", "label=Delete");
                    break;
                default:
                    break;
            }

            Submit();
        }
       
        public void CreateDraftForPublishedPost(Post post)
        {
            ManagePosts();
            var postIndex = GetPostIndexOnManagePostsPage(post);
            var actionIndex = GetActionIndexOfPostByText(postIndex, "Edit");
            ClickAndWait(String.Format("{0}[{1}]/div/div[2]/a[{2}]", PostsLocator, postIndex,actionIndex));
            ClickAndWait("//button[@value='submit.Save']");
        }

        public void DiscardDraftOfPublishedPost(Post post)
        {
            DoActionToPost(post, PostAction.Edit);
            ClickAndWait("link=Discard Draft");       
        }

        public string GetPublishStatusOfPost(Post post)
        {
            return GetPublishStatusOfPost(post.Title);
        }

        public string GetPublishStatusOfPost(string postTitle)
        {
            ManagePosts();
            int index = GetPostIndexOnManagePostsPage(postTitle);
            string publishStatusStr = selenium.GetText(String.Format("{0}[{1}]/div/div/div/ul/li[1]", PostsLocator, index));
            return publishStatusStr.Substring(0, publishStatusStr.Length - 2);
        }

        public string GetDraftStatusOfPost(Post post)
        {
            return GetDraftStatusOfPost(post.Title);
        }

        public string GetDraftStatusOfPost(string postTitle)
        {
            ManagePosts();
            int index = GetPostIndexOnManagePostsPage(postTitle);
            string draftStatusStr = selenium.GetText(String.Format("{0}[{1}]/div/div/div/ul/li[2]", PostsLocator, index));
            return draftStatusStr.Substring(0, draftStatusStr.Length - 2);
        }

        public string GetPublishDateTimeAtPostViewPage()
        {
            return selenium.GetText("//div[@class='metadata']/div[@class='published']");
        }

        public string GetOwnerOfPost(Post post)
        {
            ManagePosts();
            int index = GetPostIndexOnManagePostsPage(post);
            string ownerStr = selenium.GetText(String.Format("{0}[{1}]/div/div/div/ul[2]/li[2]", PostsLocator,index));
            return ownerStr.Split(' ')[1];            
        }      

        public void ChangePostSlug(Post post, string newSlug)
        {
            DoActionToPost(post, PostAction.Edit);       
            selenium.Type("Routable_Slug", newSlug);
            ClickAndWait("//button[@value='submit.Publish']");
        }

      
    }

    public class PostSettings
    {
        public string Title { get; set; }

        public string Permalink { get; set; }

        public bool SetAsHomePage { get; set; }

        public string Body { get; set; }

        public List<Tag> Tags { get; set; }

        public bool AllowNewComments { get; set; }

        public string Owner { get; set; }

        public PublishSettings PublishSetting { get; set; }

        public string PublishDate { get; set; }

        public string PublishTime { get; set; }

        public bool ArchiveLater { get; set; }

        public string ArchiveDate { get; set; }

        public string ArchiveTime { get; set; }

        public PostSettings()
        {
            Title = String.Format("Post {0}", UIHelperBase.GenerateUniqueIdentifier(DateTime.Now));
            Permalink = String.Format("post-{0}", UIHelperBase.GenerateUniqueIdentifier(DateTime.Now));
            PublishSetting = PublishSettings.PublishNow;
            AllowNewComments = true;
            Owner = TestLibrary.Consts.AdminUserName;
        }
    }

    public class Post : IContent
    {
        public PostSettings PostSetting { get; private set; }

        public string Title { get { return PostSetting.Title; } }

        public List<Tag> Tags { get { return PostSetting.Tags; } }

        public string Permalink { get { return PostSetting.Permalink; } }

        public Blog ContainerBlog { get; private set; }

        public int Id { get; set; }

        public Post(PostSettings postSetting, Blog blog)
        {
            PostSetting = postSetting;
            ContainerBlog = blog;
        }
    }

    public enum PostAction
    {
        View,
        Unpublish,
        Publish,
        Edit,
        Delete,
        NoAction
    }
}
